﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using SdmxMl.Common;

namespace SdmxMl.Structure
{
    /// <summary>
    /// Helper class for holding MetadataAAttributes occurences & presentational flag
    /// </summary>
    public class MetaAttributeOccurences
    {
        /// <summary> Occurence pattern availables </summary>
        public enum eTypeOccurs
        {
            T_0_1,
            T_0_n,
            T_1_1,
            T_1_n,
            T_m_n
        }

        /// <summary> String representations of occurences pattern </summary>
        public static string[] OccurencePatternStr;

        static MetaAttributeOccurences()
        {
            OccurencePatternStr = new string[] {
                "0 - 1",   // optional
                "0 - n",   // optional, max value must be specified
                "1 - 1",   // mandatory
                "1 - n",   // mandatory, max value must be specified
                "m - n"    // mandatory  min, max to be specified (min > 0)
            };
        }

        public eTypeOccurs OccurencesPattern { get; private set; }

        /// <summary>
        /// The isPresentational attribute indicates whether the metadata attribute should allow for a value.
        /// A value of true, meaning the metadata attribute is presentational means that the attribute only
        /// contains child metadata attributes, and does not contain a value. If this attribute is not set
        /// to true, and a representation (coded or uncoded) is not defined, then the representation of the
        /// metadata attribute will be inherited from the concept from which it takes its identity.
        /// </summary>
        public bool IsPresentational { get; set; }

        /// <summary>
        /// The minOccurs attribute indicates the minimum number of times this metadata attribute can occur
        /// within its parent object.
        /// </summary>
        public int MinOccurs { get; private set; }

        /// <summary>
        /// The maxOccurs attribute indicates the maximum number of times this metadata attribute can occur
        /// within its parent object.
        /// </summary>
        public int MaxOccurs { get; private set; }

        public bool MinUnspecified { get; private set; }
        public bool MaxUnspecified { get; private set; }

        /// <summary>
        /// SFO usage if true, the attribute when edited can select more than one code of the associated
        /// Codelist. selected codes are stored separated by a "semi-column"
        /// </summary>
        public bool IsMultiCodes { get; set; }

        /// <summary>
        /// For an SDMX 2.0 information representation
        /// </summary>
        public UsageStatusType UsageStatus
        {
            get
            {
                if (OccurencesPattern == eTypeOccurs.T_0_1 || OccurencesPattern == eTypeOccurs.T_0_n)
                    return UsageStatusType.Conditional;
                return UsageStatusType.Mandatory;
            }
        }

        public MetaAttributeOccurences(bool isPresentation, int minOcc, int maxOcc)
        {
            IsPresentational = isPresentation;
            MinOccurs = minOcc;
            MaxOccurs = maxOcc;

            if (MinOccurs == 0)
                OccurencesPattern = MaxOccurs == 1 ? eTypeOccurs.T_0_1 : eTypeOccurs.T_0_n;
            else if (MinOccurs == 1)
                OccurencesPattern = MaxOccurs == 1 ? eTypeOccurs.T_1_1 : eTypeOccurs.T_1_n;
            else
                OccurencesPattern = eTypeOccurs.T_m_n;

            UpdatePattern(OccurencesPattern, MinOccurs.ToString(), MaxOccurs.ToString());
        }

        /// <summary> SDMX 2.0 C'tor (we know only if mandatory or not. </summary>
        /// <param name="us"></param>
        public MetaAttributeOccurences(UsageStatusType us, bool multiCodes)
        {
            if (us == UsageStatusType.Mandatory)
            {
                OccurencesPattern = eTypeOccurs.T_1_n;
                MinOccurs = 1;
                MaxUnspecified = true;
            }
            else
            {
                OccurencesPattern = eTypeOccurs.T_0_n;
                MinOccurs = 0;
                MaxUnspecified = true;
            }

            IsMultiCodes = multiCodes;
        }

        public void UpdatePattern(eTypeOccurs pattern)
        {
            UpdatePattern(pattern, string.Empty, string.Empty);
        }

        public void UpdatePattern(eTypeOccurs pattern, string mini, string maxi)
        {
            OccurencesPattern = pattern;
            int result = 0;

            switch (pattern)
            {
                case eTypeOccurs.T_0_1:
                    MinOccurs = 0;
                    MaxOccurs = 1;
                    MinUnspecified = MaxUnspecified = false;
                    break;

                case MetaAttributeOccurences.eTypeOccurs.T_0_n:
                    MinOccurs = 0;
                    MinUnspecified = false;
                    MaxUnspecified = true;
                    MaxOccurs = 2;
                    if (int.TryParse(maxi, out result))
                    {
                        MaxUnspecified = false;
                        MaxOccurs = System.Math.Max(2, result);
                    }
                    break;

                case MetaAttributeOccurences.eTypeOccurs.T_1_1:
                    MinOccurs = MaxOccurs = 1;
                    MinUnspecified = MaxUnspecified = false;
                    break;

                case MetaAttributeOccurences.eTypeOccurs.T_1_n:
                    MinOccurs = 1;
                    MaxOccurs = 2;
                    MinUnspecified = false;
                    MaxUnspecified = true;
                    if (int.TryParse(maxi, out result))
                    {
                        MaxUnspecified = false;
                        MaxOccurs = System.Math.Max(2, result);
                    }
                    break;

                case MetaAttributeOccurences.eTypeOccurs.T_m_n:
                    MinOccurs = 1;
                    MaxOccurs = 2;
                    MinUnspecified = true;
                    MaxUnspecified = true;

                    string s = mini + ";" + maxi;
                    string[] arr = s.Split(';');
                    if (arr.Length == 2)
                    {
                        if (int.TryParse(arr[0], out result))
                        {
                            MinUnspecified = false;
                            MinOccurs = System.Math.Max(2, result);
                        }
                        if (int.TryParse(arr[1], out result))
                        {
                            MaxUnspecified = false;
                            MaxOccurs = System.Math.Max(2, result);
                        }
                    }
                    break;
            }
        }

        public override string ToString()
        {
            return OccurencePatternStr[(int)OccurencesPattern];
        }


        public void PersistToXmlByAnnotation(XmlTextWriter xw, string prefix, AnnotationList anList, TextFormatType tf)
        {
            Annotation aMultiLang = null;
            Annotation aPresentational = null;
            Annotation aMultiCodes = null;
            Annotation aOccPattern = null;

            // Is Multilingual simulation
            if (tf.TextType == TextTypeType.String && tf.isMultiLingual == false)
            {
                aMultiLang = new Annotation("SDMX21_IsMultiLingual");
                aMultiLang.Title = "False";
                anList.Add(aMultiLang);
            }

            // Is Presentational simulation
            if (IsPresentational)
            {
                aPresentational = new Annotation("SDMX21_IsPresentational");
                anList.Add(aPresentational);
            }
            else if (IsMultiCodes) // No multicodes if presentational
            {
                aMultiCodes = new Annotation("IsMultiCodes");
                anList.Add(aMultiCodes);
            }

            // Occurences
            aOccPattern = new Annotation("SDMX21_Occurences");
            aOccPattern.Title = OccurencesPattern.ToString();
            if ((OccurencesPattern == eTypeOccurs.T_0_n || OccurencesPattern == eTypeOccurs.T_1_n) &&
                MaxUnspecified == false)
            {
                aOccPattern.Text.UpdateDefaultText(MaxOccurs.ToString());
            }
            else if (OccurencesPattern == eTypeOccurs.T_m_n && (MaxUnspecified == false || MinUnspecified == false))
            {
                string m = MinUnspecified ? string.Empty : MinOccurs.ToString();
                m += ';';
                m += MaxUnspecified ? string.Empty : MaxOccurs.ToString();
                aOccPattern.Text.UpdateDefaultText(m);
            }
            anList.Add(aOccPattern);

            // Persist annotations
            anList.WriteXml(xw, prefix, null);

            // Remove annotations for SDMX 2.1 simulation
            if (aMultiLang != null)
                anList.Remove(aMultiLang);

            if (aPresentational != null)
                anList.Remove(aPresentational);

            if (aMultiCodes != null)
                anList.Remove(aMultiCodes);

            anList.Remove(aOccPattern);
        }

        /// <summary>
        /// Retrieves SDMX 2.1 informations saved in SDMX 2.0 annotations
        /// </summary>
        /// <param name="anList">The annotations container</param>
        public void RetrieveFormXmlByAnnotation(AnnotationList anList)
        {

            // Has SDMX 2.0 annotation for SDMX 2.1 mimic
            Annotation aPresentational = anList.GetAnnotationType("SDMX21_IsPresentational");
            if (aPresentational != null)
            {
                IsPresentational = true;
                anList.Remove(aPresentational);
            }

            Annotation aMultiCodes = anList.GetAnnotationType("IsMultiCodes");
            if (aMultiCodes != null)
            {
                IsMultiCodes = true;
                anList.Remove(aMultiCodes);
            }

            Annotation aOccPattern = anList.GetAnnotationType("SDMX21_Occurences");
            if (aOccPattern != null)
            {
                string s = aOccPattern.Title;
                foreach (eTypeOccurs eo in Enum.GetValues(typeof(eTypeOccurs)))
                    if (eo.ToString().ToLower() == s.ToLower())
                    {
                        OccurencesPattern = eo;
                        break;
                    }

                // a Min or max to retrieve ?
                int result = 0;
                s = aOccPattern.Text.GetFirst().Trim();
                if (OccurencesPattern == eTypeOccurs.T_0_n || OccurencesPattern == eTypeOccurs.T_1_n)
                {
                    // Defaulted
                    MaxUnspecified = true;
                    MaxOccurs = 2;
                    if (int.TryParse(s, out result))
                    {
                        MaxUnspecified = false;
                        MaxOccurs = System.Math.Max(2, result);
                    }
                    MinOccurs = OccurencesPattern == eTypeOccurs.T_0_n ? 0 : 1;
                }
                else if (OccurencesPattern == eTypeOccurs.T_m_n)
                {
                    // Defaulted
                    MinUnspecified = true;
                    MinOccurs = 2;
                    MaxUnspecified = true;
                    MaxOccurs = 2;
                    string[] arr = s.Split(';');
                    if (arr.Length == 2)
                    {
                        if (int.TryParse(arr[0], out result))
                        {
                            MinUnspecified = false;
                            MinOccurs = System.Math.Max(2, result);
                        }
                        if (int.TryParse(arr[1], out result))
                        {
                            MaxUnspecified = false;
                            MaxOccurs = System.Math.Max(2, result);
                        }
                    }
                }
                else if (OccurencesPattern == eTypeOccurs.T_1_1)
                {
                    MaxUnspecified = MinUnspecified = false;
                    MinOccurs = MaxOccurs = 1;
                }
                else if (OccurencesPattern == eTypeOccurs.T_0_1)
                {
                    MaxUnspecified = MinUnspecified = false;
                    MinOccurs = 0;
                    MaxOccurs = 1;
                }


                anList.Remove(aOccPattern);
            }
        }
            

    }
}
